iT邦幫忙

2022 iThome 鐵人賽

DAY 16
0
Mobile Development

Android app 效能優化系列 第 16

Application context 與 Activity context

  • 分享至 

  • xImage
  •  

上一篇我們介紹了一個因 Activity 被 Singleton 參考所發生的 Memory leak 。

object Class1 {
    lateinit var context: Context
}

class MainActivity : AppCompatActivity() {
    private val binding by lazy { ActivityMain2Binding.inflate(layoutInflater) }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(binding.root)

		//傳入Activity context
        Class1.initApp(this)
    }
}

會造成 Memory leak 的原因在於用錯了 Context。Singleton 的生命週期跟整個 App 的生命週期是一樣的,所以應使用 Application context 而不是 Activity context。這一篇我們就來看一下 Activity 與 Application context 的差異在哪裡?

Application context 與 Activity context

Context 是一個抽象類別,讓我們取得 Activity 與 Application 的環境資訊。例如開啟一個 Activity需要 Context,要顯示 Dialog、啟動 Service、發送 Broadcast、資料庫存取、Shared preferences 都需要 Context。

取得 Activity context:透過 View.getContext 或是在 Activity 直接用 this 取得。

val context = binding.imageView.context
val context = this

取得Application context:透過 getApplicationContext 取得。

this.applicationContext

Application Context 是跟UI無關的

例如在 Activity 要顯示 Dialog,AlertDialog.Builder(context) 裡要傳入的 Context 就必須是Activity content。

val alertDialog = AlertDialog.Builder(this)
alertDialog.setMessage("Message")
alertDialog.create().show()

如果傳入 applicationContext 是會閃退的

//傳入applicationContext會閃退
val alertDialog = AlertDialog.Builder(applicationContext)
alertDialog.setMessage("Message")
alertDialog.create().show()

生命週期不一樣

這兩個最大的差別是 Activity Context 的生命週期是跟著 Activity,Application context 的生命週期是跟著Application。在應該傳入 Application context 的地方如果傳入了 Activity context 就會造成Memory leak。

回來修正剛剛的 Singleton 範例,我們試著來修正這個 Memory leak。

object Class1 {
    lateinit var context: Context
}

Class1 既然生命週期與 App 是一樣的,所以需要的是 Application Context,我們就想辦法讓context 只能被設定為 Application context。

步驟:

  1. context 改為 priviate,使其無法直接被設定。
  2. 新增一個 initApp function 傳入 context
  3. 在這個 function 裡將 context 透過 context.applicationContext 一律轉為 Application context。即使傳入了 Activity,也會轉成 Application Context。
object Class1 {
    private lateinit var context: Context

	//如果傳入了Activity context,也不會直接放在staic,而是取得ApplicationContext
    fun initApp(context:Context){
        context = context.applicationContext
    }
}

class MainActivity : AppCompatActivity() {
    private val binding by lazy { ActivityMain2Binding.inflate(layoutInflater) }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(binding.root)

		//傳入Activity context
        Class1.initApp(this)
    }
}

這樣就修改完成了,確保拿到的一定會是 Application context。最後,誤用 Context 是最常發生的 Memory leak,一定要小心使用 Context。


上一篇
Memory leak 記憶體洩漏
下一篇
WeakReference 弱引用
系列文
Android app 效能優化30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言